package org.dromara.web.service.business;


import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.business.SettingKeyEnum;
import org.dromara.common.core.enums.business.UserAuthLevel;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.dao.business.domain.Settings;
import org.dromara.dao.business.domain.User;
import org.dromara.dao.business.domain.UserBalanceLog;
import org.dromara.dao.business.domain.UserQuery;
import org.dromara.dao.business.enums.SettingKeys;
import org.dromara.dao.business.mapper.UserBalanceLogMapper;
import org.dromara.dao.business.mapper.UserMapper;
import org.dromara.dao.system.domain.SysClient;
import org.dromara.dao.system.domain.vo.SysClientVo;
import org.dromara.web.controller.business.req.UserReq;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.plugins.udun.UDUNWalletService;
import org.dromara.web.service.RLockService;
import org.dromara.web.service.system.ISysClientService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import static java.time.temporal.ChronoUnit.MINUTES;

@Service
public class UserService {

    @Autowired
    UserMapper userMapper;
    @Autowired
    SettingsService settingsService;
    @Autowired
    RLockService rLockService;
    @Autowired
    UserBalanceLogMapper userBalanceLogMapper;
    @Autowired
    ISysClientService clientService;
    @Autowired
    UDUNWalletService udunWalletService;


    public TableDataInfo<User> queryPageList(UserQuery query, PageQuery pageQuery) {

        return new TableDataInfo(
                userMapper.paginationSelectList(query, pageQuery),
                userMapper.paginationSelectCount(query)
        );
    }


    public User queryById(Long id) {
        return userMapper.selectById(id);
    }


    @Transactional
    public User insert(User entity) {
        userMapper.insert(entity);
        return entity;
    }

    @Transactional
    public void updateById(User update) {

        User entity = userMapper.selectById(update.getId());
        if (entity == null) {
            throw new ServiceException();
        }
        entity.setUsername(update.getUsername());
        entity.setPassword(update.getPassword());
        entity.setAvatar(update.getAvatar());
        entity.setStatus(update.getStatus());
        entity.setRegisterIp(update.getRegisterIp());
        entity.setLastLoginTime(update.getLastLoginTime());
        entity.setLastLoginIp(update.getLastLoginIp());
        entity.setRemark(update.getRemark());
        entity.setUpdateTime(LocalDateTime.now());
        userMapper.updateById(entity);
    }


    @Transactional
    public void deleteById(Long id) {
        userMapper.deleteById(id);
    }

    @Transactional
    public void deleteByIds(Long[] ids) {
        userMapper.deleteByIds(Arrays.asList(ids));
    }



    @Transactional
    public User adminRegister(UserReq.UserAddReq req) {

        User user = new User();
        BeanUtils.copyProperties(req, user);


        return register(user, req.getInvitorId());
    }


    @Transactional
    public User frontRegister(UserReq.RegisterReq req, String ip) {
        Map<String, Settings> settingsMap = settingsService.getSettings();
        User user = new User();
        user.setRegisterIp(ip);
        user.setStatus(true);
        user.setPassword(req.getPassword());
        user.setUsername(req.getAccount());

        if (StringUtils.isNotEmpty(req.getInviteCode())) {
            User invitor = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getInviteCode, req.getInviteCode()));
            return register(user, invitor == null ? null : invitor.getId());
        }
        return register(user, null);
    }



    @Transactional
    public LoginVo deviceRegisterLogin(String deviceId, String ip, String clientId) {
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getDeviceId, deviceId));
        if (user == null) {
            user = new User();

            user.setDeviceId(deviceId);
            user.setUsername(deviceId);
            user.setRegisterIp(ip);
            user.setStatus(true);
            user.setPassword(getRandomCode(6));
//            user.setPasswordDeadTime(LocalDateTime.now().plus(10, MINUTES));

            register(user, null);
        }

        SysClientVo client = clientService.queryByClientId(clientId);

        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getId());
        loginUser.setUserType(client.getClientKey());
        loginUser.setUsername(user.getUsername());
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());

        SaLoginModel model = new SaLoginModel();
        model.setDevice(client.getDeviceType());
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
        // 例如: 后台用户30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // 生成token
        LoginHelper.login(loginUser, model);

        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());

        return loginVo;

    }



    public LoginVo pcLogin(String username, String password, String clientId) {
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
        if (user == null) {
            throw new UserException("user.not.exists", username);
        }
        if (user.getStatus() != null && !user.getStatus()) {
            throw new UserException("user.blocked", username);
        }
//        if (!password.equals(user.getPassword()) || user.getPasswordDeadTime() == null || user.getPasswordDeadTime().isBefore(LocalDateTime.now())) {
//            throw new UserException("user.password.wrong");
//        }

        if (!password.equals(user.getPassword()) ) {
            throw new UserException("user.password.wrong");
        }


        SysClientVo client = clientService.queryByClientId(clientId);

        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getId());
        loginUser.setUserType(client.getClientKey());
        loginUser.setUsername(user.getUsername());
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());

        SaLoginModel model = new SaLoginModel();
        model.setDevice(client.getDeviceType());
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
        // 例如: 后台用户30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // 生成token
        LoginHelper.login(loginUser, model);

        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());

        return loginVo;
    }


    @Transactional
    public User refreshPassword(Long userId) {
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getId, userId));
        user.setPassword(getRandomCode(6));
//        user.setPasswordDeadTime(LocalDateTime.now().plus(10, MINUTES));
        userMapper.updateById(user);
        return user;
    }





    @Transactional
    public User register(User user, Long invitorId) {

        user.setInviteCode(getValidInviteCode(6));
        user.setInviteUserId(invitorId);


        Map<String, String> settings = settingsService.getSettingsMap();
        Integer diamondCount = 0;
        Integer starCount = 0;
        Integer registerDiamondCount = 0;
        Integer registerStarCount = 0;
        try {
            if (invitorId != null) {
                List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getInviteUserId, invitorId));
                if (CollectionUtil.isEmpty(users)) {
                    diamondCount = Integer.parseInt(settings.get(SettingKeys.INVITE_DIAMOND_COUNT));
                    starCount = Integer.parseInt(settings.get(SettingKeys.INVITE_STAR_COUNT));
                } else {
                    diamondCount = Integer.parseInt(settings.get(SettingKeys.INVITE_MORE_DIAMOND_COUNT));
                    starCount = Integer.parseInt(settings.get(SettingKeys.INVITE_MORE_DIAMOND_COUNT));
                }
            }
            registerDiamondCount = Integer.parseInt(settings.get(SettingKeys.INVITE_REGISTER_DIAMOND_COUNT));
        }catch (Exception e) {

        }
        if ((diamondCount > 0 || starCount > 0)) {
            userBalanceChange(invitorId, diamondCount, starCount, "邀请注册奖励");
        }
        if (registerDiamondCount > 0) {
            user.setBalanceDiamond(registerDiamondCount);

        }
        userMapper.insert(user);

        if (registerDiamondCount > 0) {
            UserBalanceLog userBalanceLog = new UserBalanceLog();
            userBalanceLog.setUserId(user.getId());
            userBalanceLog.setRemark("受邀请奖励");
            userBalanceLog.setDiamondBefore(0);
            userBalanceLog.setDiamondDelta(registerDiamondCount);
            userBalanceLog.setDiamondAfter(registerDiamondCount);
            userBalanceLog.setStarBefore(0);
            userBalanceLog.setStarDelta(0);
            userBalanceLog.setStarAfter(0);
            userBalanceLogMapper.insert(userBalanceLog);
        }

        return user;
    }



    private String getValidInviteCode(int length) {
        while (true) {
            String code = getRandomCode(length);

            User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getInviteCode, code));
            if (user == null) {
                return code;
            }
        }
    }


    private String getRandomCode(int length) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }



    @Transactional
    public void userBalanceChange(Long userId, Integer diamond, Integer star, String remark) {
        rLockService.lockUserWallet(userId, (lock)->{
            User user = userMapper.selectById(userId);

            if (user.getBalanceDiamond() + diamond < 0 || user.getBalanceStar() + star < 0) {
                throw new UserException("user.balance.not_enough");
            }



            UserBalanceLog userBalanceLog = new UserBalanceLog();
            userBalanceLog.setUserId(userId);
            userBalanceLog.setDiamondBefore(user.getBalanceDiamond());
            userBalanceLog.setStarBefore(user.getBalanceStar());

            user.setBalanceDiamond(user.getBalanceDiamond() + diamond);
            user.setBalanceStar(user.getBalanceStar() + star);

            userBalanceLog.setDiamondAfter(user.getBalanceDiamond());
            userBalanceLog.setStarAfter(user.getBalanceStar());
            userBalanceLog.setRemark(remark);
            userBalanceLog.setDiamondDelta(diamond);
            userBalanceLog.setStarDelta(star);
            userMapper.updateById(user);

            userBalanceLogMapper.insert(userBalanceLog);
        });
    }



    @Transactional
    public void updateUserName(Long userId, String name) {
        User user = userMapper.selectById(userId);
        user.setName(name);
        userMapper.updateById(user);
    }

    @Transactional
    public void updateAvatar(Long userId, String avatar) {
        User user = userMapper.selectById(userId);
        user.setAvatar(avatar);
        userMapper.updateById(user);
    }

    @Transactional
    public void telegramUserRegister(String username) {
        telegramUserInfo(username);
    }


    public String telegramUserHighAddress(String username) {
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
        if (StringUtils.isEmpty(user.getUdunAddressHigh())) {
            return "aaaaaaa";
//            String address = udunWalletService.createAddress();
//            user.setUdunAddressHigh(address);
//            userMapper.updateById(user);
        }
        return user.getUdunAddressHigh();
    }

    public String telegramUserLowAddress(String username) {
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
        if (StringUtils.isEmpty(user.getUdunAddressLow())) {
            return "bbbbb";
//            String address = udunWalletService.createAddress();
//            user.setUdunAddressHigh(address);
//            userMapper.updateById(user);
        }
        return user.getUdunAddressLow();
    }


    public User telegramUserInfo(String username) {
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
        if (user == null) {
            user = new User();
            user.setUsername(username);
            user.setStatus(true);
            user.setBalanceDiamond(0);
            user.setBalanceStar(0);
            user.setUserType(0);

            userMapper.insert(user);
        }
        return user;
    }
}
